﻿using CleanArchitecture.Core.Configuration;
using Lucene.Net.Documents;
using Masuit.LuceneEFCore.SearchEngine;
using Masuit.LuceneEFCore.SearchEngine.Extensions;
using Masuit.LuceneEFCore.SearchEngine.Interfaces;
using Newtonsoft.Json;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;

namespace CleanArchitecture.Core.SharedKernel
{
    // This can be modified to BaseEntity<TId> to support multiple key types (e.g. Guid)
    //DavidLee 2020/03/18 整合Masuit.LuceneEFCore.SearchEngine，
    //单独重写Masuit.LuceneEFCore.SearchEngine.LuceneIndexableBaseEntity
    public abstract class BaseEntity: ILuceneIndexable
    {
        [LuceneIndex(Name = "Id", Store = Field.Store.YES /*Index = Field.Index.NOT_ANALYZED*/)]
        public int Id { get; set; }
        
        public SystemLogLevel SysLogLvl { get; set; }
        /// <summary>
        /// 通过DomainEventDispatcher保存额外的日志消息，不与数据库表有实际映射关系
        /// </summary>
        public string SysLogMsg { get; set; }
        /// <summary>
        /// 存储数据库对象额外信息，不与数据库表有实际映射关系 
        /// </summary>
        public object ExtraInfo { get; set; }

        //
        // 摘要:
        //     索引唯一id
        [LuceneIndex(Name = "IndexId", Store = Field.Store.YES/*, Index = Field.Index.NOT_ANALYZED*/)]
        [NotMapped, JsonIgnore]
        public string IndexId
        {
            get => GetType().Name + ":" + Id;
            set { }
        }

        /// <summary>
        /// 转换成Lucene文档
        /// </summary>
        /// <returns></returns>
        //public virtual Document ToDocument()
        //{
        //    var doc = new Document();
        //    var type = GetType();
        //    if (type.Assembly.IsDynamic && type.FullName.Contains("Prox"))
        //    {
        //        type = type.BaseType;
        //    }

        //    var classProperties = type.GetProperties();
        //    //Creates a new Lucene.Net.Documents.StringField (a field that is indexed but not tokenized)
        //    //doc.Add(new Field("Type", type.AssemblyQualifiedName, Field.Store.YES, Field.Index.NOT_ANALYZED));
        //    doc.Add(new StringField("Type", type.AssemblyQualifiedName, Field.Store.YES));
        //    foreach (var propertyInfo in classProperties)
        //    {
        //        var propertyValue = propertyInfo.GetValue(this);
        //        if (propertyValue == null) continue;

        //        var attrs = propertyInfo.GetCustomAttributes<LuceneIndexAttribute>();
        //        foreach (var attr in attrs)
        //        {
        //            string name = !string.IsNullOrEmpty(attr.Name) ? attr.Name : propertyInfo.Name;
        //            string value = attr.IsHtml ? propertyValue.ToString().RemoveHtmlTag() : propertyValue.ToString();
        //            Field docfield;
        //            if (attr.Index == Field.Index.NOT_ANALYZED)
        //                docfield = new StringField(name, value, attr.Store);
        //            else 
        //                docfield = new TextField(name, value, attr.Store);
        //            doc.Add(docfield);
        //        }
        //    }
        //    return doc;
        //}
        /// <summary>
        /// 转换成Lucene文档
        /// </summary>
        /// <returns></returns>
        public virtual Document ToDocument()
        {
            var doc = new Document();
            var type = GetType();
            if (type.Assembly.IsDynamic && type.FullName.Contains("Prox"))
            {
                type = type.BaseType;
            }

            var classProperties = type.GetProperties();
            doc.Add(new StringField("Type", type.AssemblyQualifiedName, Field.Store.YES));
            foreach (var propertyInfo in classProperties)
            {
                var propertyValue = propertyInfo.GetValue(this);
                if (propertyValue == null)
                {
                    continue;
                }

                var attrs = propertyInfo.GetCustomAttributes<LuceneIndexAttribute>();
                foreach (var attr in attrs)
                {
                    string name = !string.IsNullOrEmpty(attr.Name) ? attr.Name : propertyInfo.Name;
                    string value = attr.IsHtml ? propertyValue.ToString().RemoveHtmlTag() : propertyValue.ToString();
                    switch (propertyValue)
                    {
                        case string _ when value.Length < 8191:
                            doc.Add(new StringField(name, value, attr.Store));
                            break;
                        case string _:
                            doc.Add(new TextField(name, value, attr.Store));
                            break;
                        case int num:
                            doc.Add(new Int32Field(name, num, attr.Store));
                            break;
                        case long num:
                            doc.Add(new Int64Field(name, num, attr.Store));
                            break;
                        case float num:
                            doc.Add(new SingleField(name, num, attr.Store));
                            break;
                        case decimal num:
                            doc.Add(new DoubleField(name, (double)num, attr.Store));
                            break;
                        case double num:
                            doc.Add(new DoubleField(name, num, attr.Store));
                            break;
                        case DateTime _:
                            doc.Add(new StringField(name, value, attr.Store));
                            break;
                        default:
                            doc.Add(new TextField(name, value, attr.Store));
                            break;
                    }
                }
            }

            return doc;
        }
    }
}